package com.ouc.eddyAlgorithm.dao.impl;

import com.ouc.common.constant.VariableName;
import com.ouc.common.entity.NcData;
import com.ouc.common.ncRead.NcDao;
import com.ouc.common.utils.ArrayUtils;
import com.ouc.common.utils.StringUtils;
import com.ouc.common.utils.file.FileUtils;
import com.ouc.common.utils.nc.NcUtil;
import com.ouc.eddyAlgorithm.dao.VGDao;
import com.ouc.eddyAlgorithm.domain.VgEddy;
import com.ouc.eddyAlgorithm.entity.VGGridData;
import com.ouc.eddyAlgorithm.utils.VGEddyDetectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;

import java.io.IOException;
import java.text.ParseException;
import java.util.*;

import static com.ouc.common.constant.NcConstants.UU;
import static com.ouc.common.constant.NcConstants.VV;


@Repository
public class VGDaoImpl implements VGDao {
  @Autowired
  private NcDao ncDao;

  @Override
  public VGGridData getVGGridData(NetcdfFile file, int a, int b, int level, boolean[] constraints, Float[] lonLimit, Float[] latLimit) throws IOException, InvalidRangeException {
    VGGridData vgGridData = new VGGridData();
    Variable uFlow = ncDao.getVariable(file, VariableName.UFLOW);
    Variable vFlow = ncDao.getVariable(file, VariableName.VFLOW);
    Variable lon = ncDao.getVariable(file, VariableName.LON);
    Variable lat = ncDao.getVariable(file, VariableName.LAT);
    float[][] u2DData = null;
    float[][] v2DData = null;
    //  下面是之前的代码
    Map<String, NcData> map = ncDao.getNcDataOfLimit(file, lonLimit, latLimit, level);
    /*Map<String, NcData> map = ncDao.getNcDataOfLimitFromVariable(file, UU, lonLimit, latLimit, level);
    Map<String, NcData> mapV = ncDao.getNcDataOfLimitFromVariable(file, VV, lonLimit, latLimit, level);*/
//    map.put(vFlow.getShortName(), mapV.get(vFlow.getShortName()));
    for (Map.Entry<String, NcData> entry : map.entrySet()) {
      if (entry.getKey().equals(lon.getShortName())) {
        vgGridData.setListLon(entry.getValue().getData1D());
        continue;
      }
      if (entry.getKey().equals(lat.getShortName())) {
        vgGridData.setListLat(entry.getValue().getData1D());
        continue;
      }
      if (entry.getKey().equals(uFlow.getShortName())) {
        u2DData = entry.getValue().getData2D();
        continue;
      }
      if (entry.getKey().equals(vFlow.getShortName())) {
        v2DData = entry.getValue().getData2D();
        continue;
      }
    }

    float[][] w2DData = new float[u2DData.length][u2DData[0].length];
    for (int i = 0; i < u2DData.length; i++) {
      for (int j = 0; j < u2DData[0].length; j++) {
        w2DData[i][j] = (float) Math.sqrt(Math.pow(u2DData[i][j], 2) + Math.pow(v2DData[i][j], 2));
      }
    }


    int[][] vgEddy = VGEddyDetectUtil.getVgEddy(v2DData, u2DData, w2DData, a, b, constraints);
    float[][] vgEddyRadius = VGEddyDetectUtil.getVgEddyRadius(vgEddy, v2DData, u2DData);
    List<int[]>[][] vgEddyBoundary = VGEddyDetectUtil.getVgEddyBoundary(vgEddy, v2DData, u2DData);
//    int[][] boundaryPolygon = VGEddyDetectUtil.getVgEddyBoundaryPolygon(vgEddy, v2DData, u2DData);

    vgGridData.setListUFlow(ArrayUtils.flat2D(u2DData));
    vgGridData.setListVFlow(ArrayUtils.flat2D(v2DData));
    vgGridData.setListFlow(ArrayUtils.flat2D(w2DData));
    vgGridData.setValues(ArrayUtils.flat2D(vgEddy));
    vgGridData.setEddyRadius(ArrayUtils.flat2D(vgEddyRadius));
    vgGridData.setBoundary(vgEddyBoundary);
    vgGridData.setVariableName("vg");


    return vgGridData;
  }

  //实际层级与VgEddy对象层级的映射
  private int[] levelArr = {0, 14, 19, 21, 22, 23, 24, 25, 26};
  private int[] MeterArr = {0, 50, 100, 150, 200, 250, 300, 350, 400};
/*
  private int[] levelArr = {0, 5, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
  private int[] MeterArr = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1250, 1500, 2000};
*/

  @Override
  public List<VgEddy> getVgEddyMatrix(NetcdfFile file, int a, int b, int level, boolean[] constraints, int startEddyId) throws IOException, InvalidRangeException {
    Variable uFlow = ncDao.getVariable(file, VariableName.UFLOW);
    Variable vFlow = ncDao.getVariable(file, VariableName.VFLOW);
    Variable lon = ncDao.getVariable(file, VariableName.LON);
    Variable lat = ncDao.getVariable(file, VariableName.LAT);

    List<Variable> variables = Arrays.asList(lon, lat, uFlow, vFlow);

    Map<String, NcData> map = NcUtil.getAllDataOfSlice(variables, levelArr[level]);
    float[] lonList = null;
    float[] latList = null;
    float[][] u2DData = null;
    float[][] v2DData = null;

    for (Map.Entry<String, NcData> entry : map.entrySet()) {
      if (entry.getKey().matches(".*lon.*")) {
        lonList = entry.getValue().getData1D();
        continue;
      }
      if (entry.getKey().matches(".*lat.*")) {
        latList = entry.getValue().getData1D();
        continue;
      }
      if (entry.getKey().matches(".*u.*")) {
        u2DData = entry.getValue().getData2D();
        continue;
      }
      if (entry.getKey().matches(".*v.*")) {
        v2DData = entry.getValue().getData2D();
        continue;
      }
    }

    int m = u2DData.length, n = u2DData[0].length;
    float[][] w2DData = new float[m][n];
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        w2DData[i][j] = (float) Math.sqrt(Math.pow(u2DData[i][j], 2) + Math.pow(v2DData[i][j], 2));
      }
    }

    float x = lonList[1] - lonList[0];
    float y = latList[1] - latList[0];
    int[][] vgEddy = VGEddyDetectUtil.getVgEddy(v2DData, u2DData, w2DData, a, b, constraints);
    float[][] vgEddyRadius = VGEddyDetectUtil.getVgEddyRadius(vgEddy, v2DData, u2DData);
    List<int[]>[][] vgEddyBoundary = VGEddyDetectUtil.getVgEddyBoundary(vgEddy, v2DData, u2DData);
    float[][] vgEddyAmp = VGEddyDetectUtil.getVgEddyAmp(vgEddy, v2DData, u2DData, x, y);
    float[][] vgEddyEke = VGEddyDetectUtil.getVgEddyEke(vgEddy, v2DData, u2DData);
    float[][] vgEddyDens = VGEddyDetectUtil.getVgEddyDens(vgEddy, vgEddyEke, vgEddyRadius);
    List<VgEddy> vgEddies = new ArrayList<>();

    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        if ((vgEddy[i][j] == 1 || vgEddy[i][j] == 2) && vgEddyRadius[i][j] >= a) {
          vgEddies.add(new VgEddy(null, startEddyId++, FileUtils.getFileNameNoEx(file.getLocation().substring(file.getLocation().lastIndexOf("\\") + 1)), vgEddy[i][j], vgEddyRadius[i][j],
              vgEddyBoundaryToString(vgEddyBoundary[i][j]), i, latList[i], j, lonList[j], level, MeterArr[level], u2DData[i][j], v2DData[i][j], w2DData[i][j], vgEddyAmp[i][j], vgEddyEke[i][j], vgEddyDens[i][j]));
        }
      }
    }

    return vgEddies;
  }

  @Override
  public List<VgEddy> getVgEddyMatrixAll(NetcdfFile file, int a, int b, boolean[] constraints, int k, int eddyStartId) throws IOException, InvalidRangeException, ParseException {
    Variable uFlow = ncDao.getVariable(file, VariableName.UFLOW);
    Variable vFlow = ncDao.getVariable(file, VariableName.VFLOW);
    if (uFlow == null || vFlow == null) {
      return Collections.emptyList();
    }
    Variable lon = ncDao.getVariable(file, VariableName.LON);
    Variable lat = ncDao.getVariable(file, VariableName.LAT);
    //  获取变量集合
    List<Variable> variables = Arrays.asList(lon, lat, uFlow, vFlow);
    Variable level = ncDao.getVariable(file, VariableName.LEVEL);
    //  获取所有深度集合
    float[] levelMeter = NcUtil.getDataOfSlice(level).getData1D();
    List<VgEddy> vgEddies = new ArrayList<>();

    Map<String, NcData> map = NcUtil.getAllDataOfSlice(variables, k);
    float[] lonList = null;
    float[] latList = null;
    float[][] u2DData = null;
    float[][] v2DData = null;
    //  设置u数据设置v数据和经度纬度
    for (Map.Entry<String, NcData> entry : map.entrySet()) {
      if (entry.getKey().matches(".*lon.*")) {
        lonList = entry.getValue().getData1D();
        continue;
      }
      if (entry.getKey().matches(".*lat.*")) {
        latList = entry.getValue().getData1D();
        continue;
      }
      if (entry.getKey().matches(".*u.*")) {
        u2DData = entry.getValue().getData2D();
        continue;
      }
      if (entry.getKey().matches(".*v.*")) {
        v2DData = entry.getValue().getData2D();
        continue;
      }
    }

    int m = u2DData.length, n = u2DData[0].length;
    float[][] w2DData = new float[m][n];
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        w2DData[i][j] = (float) Math.sqrt(Math.pow(u2DData[i][j], 2) + Math.pow(v2DData[i][j], 2));
      }
    }
    float x = lonList[1] - lonList[0];
    float y = latList[1] - latList[0];
    int[][] vgEddy = VGEddyDetectUtil.getVgEddy(v2DData, u2DData, w2DData, a, b, constraints);
    float[][] vgEddyRadius = VGEddyDetectUtil.getVgEddyRadius(vgEddy, v2DData, u2DData);
    List<int[]>[][] vgEddyBoundary = VGEddyDetectUtil.getVgEddyBoundary(vgEddy, v2DData, u2DData);
    float[][] vgEddyAmp = VGEddyDetectUtil.getVgEddyAmp(vgEddy, v2DData, u2DData, x, y);
    float[][] vgEddyEke = VGEddyDetectUtil.getVgEddyEke(vgEddy, v2DData, u2DData);
    float[][] vgEddyDens = VGEddyDetectUtil.getVgEddyDens(vgEddy, vgEddyEke, vgEddyRadius);
    String ncTime = ncDao.getNcTime(file);
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        if ((vgEddy[i][j] == 1 || vgEddy[i][j] == 2) && vgEddyRadius[i][j] >= a) {
          vgEddies.add(new VgEddy(null, eddyStartId++, ncTime, vgEddy[i][j], vgEddyRadius[i][j],
              vgEddyBoundaryToString(vgEddyBoundary[i][j]), i, latList[i], j, lonList[j], k, levelMeter[k], u2DData[i][j], v2DData[i][j], w2DData[i][j], vgEddyAmp[i][j], vgEddyEke[i][j], vgEddyDens[i][j]));
        }
      }
    }
    return vgEddies;
  }


  @Override
  public List<List<VgEddy>> getVgEddyMatrixWithLimitLevel(NetcdfFile file, int a, int b, boolean[] constraints, Float[] lonLimit, Float[] latLimit, Integer[] levels, int eddyStartId) throws IOException, InvalidRangeException {
    //  获取深度数据
    Map<Integer, Float> levelMap = ncDao.getLevelMap(file);
    //  创建之后可能用到的集合
    List<List<VgEddy>> vgEddies = new ArrayList<>();
    float[] lonList = null;
    float[] latList = null;
    Map<String, NcData> ncDataOfLimitFromVariable = ncDao.getNcDataOfLimitFromVariable(file, UU, lonLimit, latLimit);
    //  通过遍历找到相应的经度纬度数据
    for (Map.Entry<String, NcData> entry : ncDataOfLimitFromVariable.entrySet()) {
      if (entry.getKey().matches(".*lon.*")) {
        lonList = entry.getValue().getData1D();
        continue;
      }
      if (entry.getKey().matches(".*lat.*")) {
        latList = entry.getValue().getData1D();
        continue;
      }
    }
    if (levels == null) {
      levels = new Integer[]{0, levelMap.size() - 1};
    }
    //  获取三维数据
    float[][][] data3DU = ncDao.getData3DWithLimit(file, UU, lonLimit, latLimit).getData3D();
    float[][][] data3DV = ncDao.getData3DWithLimit(file, VV, lonLimit, latLimit).getData3D();

    float[][][] data3DW = new float[levels[1] - levels[0] + 1][data3DU[0].length][data3DU[0][0].length];
    int levelIndex = 0;
    for (int i = levels[0]; i <= levels[1]; i++) {
      for (int j = 0; j < data3DW[0].length; j++) {
        for (int k = 0; k < data3DW[0][0].length; k++) {
          data3DW[levelIndex][j][k] = (float) Math.sqrt(Math.pow(data3DU[i][j][k], 2) + Math.pow(data3DV[i][j][k], 2));
        }
      }
      levelIndex++;
    }


    float x = lonList[1] - lonList[0];
    float y = latList[1] - latList[0];
    levelIndex = 0;
    for (int i = levels[0]; i <= levels[1]; i++) {
      int[][] vgEddy = VGEddyDetectUtil.getVgEddy(data3DV[i], data3DU[i], data3DW[levelIndex], a, b, constraints);
      float[][] vgEddyRadius = VGEddyDetectUtil.getVgEddyRadius(vgEddy, data3DV[i], data3DU[i]);
      List<int[]>[][] vgEddyBoundary = VGEddyDetectUtil.getVgEddyBoundary(vgEddy, data3DV[i], data3DU[i]);
      float[][] vgEddyAmp = VGEddyDetectUtil.getVgEddyAmp(vgEddy, data3DV[i], data3DU[i], x, y);
      float[][] vgEddyEke = VGEddyDetectUtil.getVgEddyEke(vgEddy, data3DV[i], data3DU[i]);
      float[][] vgEddyDens = VGEddyDetectUtil.getVgEddyDens(vgEddy, vgEddyEke, vgEddyRadius);
      List<VgEddy> tempList = new ArrayList<>();
      for (int j = 0; j < data3DW[levelIndex].length; j++) {
        for (int k = 0; k < data3DW[levelIndex][j].length; k++) {
          if ((vgEddy[j][k] == 1 || vgEddy[j][k] == 2)) {
            //&& vgEddyRadius[j][k] >= a
            tempList.add(new VgEddy(null, eddyStartId++, StringUtils.getContinuityNum(FileUtils.getName(file.getLocation()), 8), vgEddy[j][k], vgEddyRadius[j][k],
                vgEddyBoundaryToString(vgEddyBoundary[j][k]), j, latList[j], k, lonList[k], i, levelMap.get(i), data3DU[i][j][k], data3DV[i][j][k], data3DW[levelIndex][j][k], vgEddyAmp[j][k], vgEddyEke[j][k], vgEddyDens[j][k]));

          }
        }
      }
      vgEddies.add(tempList);
      levelIndex++;
    }
    return vgEddies;

  }


  private String vgEddyBoundaryToString(List<int[]> vgEddyBoundary) {
    StringBuilder sb = new StringBuilder();
    sb.append("[");
    for (int[] ints : vgEddyBoundary) {
      sb.append(Arrays.toString(ints)).append(",");
    }
    sb.deleteCharAt(sb.length() - 1);
    sb.append("]");
    return sb.toString();
  }

  @Override
  public float[][] getSurfaceData(NetcdfFile file, String variableName, int level) throws IOException, InvalidRangeException {
    Variable variable = ncDao.getVariable(file, variableName);
    NcData ncData = NcUtil.getDataOfSlice(variable, level);
    return ncData.getData2D();
  }

}
